package vip.george.realm;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import vip.george.entity.User;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;

/**
 * @author gehaodong
 * @since 2024/3/11
 * <p>
 *     一个认证+授权的Realm
 *     注意：
 *     这里继承的是AuthorizingRealm，
 *     它是AuthenticatingRealm的子类，多了一个doGetAuthorizationInfo()方法，支持为用户进行授权
 * </P>
 */
public class MyRealm03 extends AuthorizingRealm {
    private final Map<String, User> userMap = new HashMap<>();

    public MyRealm03() {
        userMap.put("tom", new User(1, "tom", "tom123"));
        userMap.put("jerry", new User(2, "jerry", "jerry123"));
    }

    /**
     * <p>授权时核心的方法</p>
     * @param principals 保留登陆用户的身份信息(类型取决于认证时存储的信息)
     * @return 返回值就是用户的角色/权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        HashSet<String> roles = new HashSet<>();
        //如果用户是tom,则给其授予'super_admin'角色
        if ("tom".equals(user.getUsername())){
            roles.add("super_admin");
        }
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        simpleAuthorizationInfo.setRoles(roles);
        return simpleAuthorizationInfo;
    }

    /**
     * <p>认证时核心的方法</p>
     * <p>这个方法就干一件事：根据<strong>用户输入的用户名</strong>去数据库查询用户信息并返回</p>
     * <p>至于密码的校验，后续shiro会自动对比</p>
     * @param token 参数是用户登录时输入的用户名和密码等信息
     * @return 返回的是从数据库中查询到的真实用户信息
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        // 获取用户登陆输入的用户名
        String username = usernamePasswordToken.getUsername();
        User userFromDB = selectUserFromDB(username);
        if (Objects.isNull(userFromDB)) {
            throw new UnknownAccountException("用户名输入错误");
        }
        return new SimpleAuthenticationInfo(userFromDB, userFromDB.getPassword(), getName());
    }


    /**
     * 模拟通过用户名从数据库中查询数据
     * @return
     */
    private User selectUserFromDB(String username) {
        return userMap.get(username);
    }
}
